package com.imooc.coupon.executor;

import com.imooc.coupon.constant.CouponCategory;
import com.imooc.coupon.constant.RuleFlag;
import com.imooc.coupon.exception.CouponException;
import com.imooc.coupon.vo.SettlementInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

import java.util.*;

/**
 * <h1>优惠券结算规则执行管理器</h1>
 * 即根据用户的请求(SettlementInfo)找到对应的 Executor, 去做结算
 * BeanPostProcessor: Bean 后置处理器
 * Created by Qinyi.
 */
@Slf4j
@Component
public class ExecuteManager implements BeanPostProcessor {

    /** 规则执行器映射 */
    private Map<RuleFlag,IRuleExecutor> executorIndex = new HashMap<>();

    /**
     * <h2>优惠券结算规则计算入口</h2>
     * 根据settlementInfo参数中包含的优惠券的类型，来调用对应的规则执行器执行。
     * 注意: 一定要保证传递进来的优惠券个数 >= 1
     * */
    public SettlementInfo computeRule(SettlementInfo settlementInfo) throws CouponException {
        SettlementInfo result = null;

        List<SettlementInfo.CouponAndTemplateInfo> couponAndTemplateInfos = settlementInfo.getCouponAndTemplateInfos();
        if(couponAndTemplateInfos.size()==1){
            CouponCategory category = CouponCategory.of(couponAndTemplateInfos.get(0).getTemplate().getCategory());
            switch (category){
                case MANJIAN:
                    result = executorIndex.get(RuleFlag.MANJIAN).computeRule(settlementInfo);
                    break;
                case LIJIAN:
                    result = executorIndex.get(RuleFlag.LIJIAN).computeRule(settlementInfo);
                    break;
                case ZHEKOU:
                    result = executorIndex.get(RuleFlag.ZHEKOU).computeRule(settlementInfo);
                    break;
                default:
                    throw new CouponException("Not Support For Other " +
                            "Template Category");
            }
        }else if(couponAndTemplateInfos.size()>1){
            // 多类优惠券,目前只支持满减+折扣
            List<CouponCategory> categories = new ArrayList<>(settlementInfo.getCouponAndTemplateInfos().size());
            settlementInfo.getCouponAndTemplateInfos().forEach(ct -> {
                categories.add(CouponCategory.of(ct.getTemplate().getCategory()));
            });
            if (categories.size() != 2) {
                throw new CouponException("Not Support For More " +
                        "Template Category");
            }else{
                if(categories.contains(CouponCategory.MANJIAN) && categories.contains(CouponCategory.ZHEKOU)){
                    result = executorIndex.get(RuleFlag.MANJIAN_ZHEKOU).computeRule(settlementInfo);
                }else{
                    throw new CouponException("Not Support For Other " +
                            "Template Category");
                }
            }
        }else{
            throw new CouponException("settlementInfo's ouponAndTemplateInfo error");
        }

        return result;
    }

    /**
     * <h2>在 bean 初始化之前去执行(before)</h2>
     * 功能：将spring中注册的IRuleExecutor组件，汇总到executorIndex中
     * */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {

        if( !(bean instanceof IRuleExecutor)){
            return bean;
        }

        IRuleExecutor ruleExecutor = (IRuleExecutor)bean;
        RuleFlag ruleFlag = ruleExecutor.ruleConfig();

        // 编写了多个相同类型的执行器，这是不允许的
        if(executorIndex.containsKey(ruleFlag)){
            throw new IllegalStateException("There is already an executor" +
                    "for rule flag: " + ruleFlag);
        }

        log.info("Load executor {} for rule flag {}.",
                ruleExecutor.getClass(), ruleFlag);
        executorIndex.put(ruleFlag,ruleExecutor);

        return null; // 返回 null 会直接中断当前 Bean 的生命周期，使其不被容器管理。
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }
}
